1use super::bse::*;
3use super::dsc::*;
4use crate::ext::io::*;
5use crate::ext::mutex::*;
6use crate::scripts::base::*;
7use crate::types::*;
8use crate::utils::encoding::encode_string;
9use crate::utils::struct_pack::*;
10use crate::utils::threadpool::*;
11use anyhow::Result;
12use msg_tool_macro::*;
13use std::collections::HashMap;
14use std::io::{Read, Seek, SeekFrom, Write};
15use std::ops::DerefMut;
16use std::sync::{Arc, Mutex};
17
18#[derive(Debug)]
19pub struct BgiArchiveBuilder {}
21
22impl BgiArchiveBuilder {
23 pub const fn new() -> Self {
25 BgiArchiveBuilder {}
26 }
27}
28
29impl ScriptBuilder for BgiArchiveBuilder {
30 fn default_encoding(&self) -> Encoding {
31 Encoding::Cp932
32 }
33
34 fn default_archive_encoding(&self) -> Option<Encoding> {
35 Some(Encoding::Cp932)
36 }
37
38 fn build_script(
39 &self,
40 data: Vec<u8>,
41 filename: &str,
42 _encoding: Encoding,
43 archive_encoding: Encoding,
44 config: &ExtraConfig,
45 _archive: Option<&Box<dyn Script>>,
46 ) -> Result<Box<dyn Script + Send + Sync>> {
47 Ok(Box::new(BgiArchive::new(
48 MemReader::new(data),
49 archive_encoding,
50 config,
51 filename,
52 )?))
53 }
54
55 fn build_script_from_file(
56 &self,
57 filename: &str,
58 _encoding: Encoding,
59 archive_encoding: Encoding,
60 config: &ExtraConfig,
61 _archive: Option<&Box<dyn Script>>,
62 ) -> Result<Box<dyn Script + Send + Sync>> {
63 if filename == "-" {
64 let data = crate::utils::files::read_file(filename)?;
65 Ok(Box::new(BgiArchive::new(
66 MemReader::new(data),
67 archive_encoding,
68 config,
69 filename,
70 )?))
71 } else {
72 let f = std::fs::File::open(filename)?;
73 let reader = std::io::BufReader::new(f);
74 Ok(Box::new(BgiArchive::new(
75 reader,
76 archive_encoding,
77 config,
78 filename,
79 )?))
80 }
81 }
82
83 fn build_script_from_reader<'a>(
84 &self,
85 reader: Box<dyn ReadSeek + Send + Sync + 'a>,
86 filename: &str,
87 _encoding: Encoding,
88 archive_encoding: Encoding,
89 config: &ExtraConfig,
90 _archive: Option<&Box<dyn Script>>,
91 ) -> Result<Box<dyn Script + Send + Sync + 'a>> {
92 Ok(Box::new(BgiArchive::new(
93 reader,
94 archive_encoding,
95 config,
96 filename,
97 )?))
98 }
99
100 fn extensions(&self) -> &'static [&'static str] {
101 &["arc"]
102 }
103
104 fn script_type(&self) -> &'static ScriptType {
105 &ScriptType::BGIArcV2
106 }
107
108 fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
109 if buf_len >= 12 && buf.starts_with(b"BURIKO ARC20") {
110 return Some(255);
111 }
112 None
113 }
114
115 fn is_archive(&self) -> bool {
116 true
117 }
118
119 fn create_archive(
120 &self,
121 filename: &str,
122 files: &[&str],
123 encoding: Encoding,
124 config: &ExtraConfig,
125 ) -> Result<Box<dyn Archive>> {
126 let f = std::fs::File::create(filename)?;
127 let writer = std::io::BufWriter::new(f);
128 Ok(Box::new(BgiArchiveWriter::new(
129 writer, files, encoding, config,
130 )?))
131 }
132}
133
134#[derive(Clone, Debug, StructPack, StructUnpack)]
135struct BgiFileHeader {
136 #[fstring = 0x60]
137 filename: String,
138 offset: u32,
139 size: u32,
140 #[fvec = 8]
141 _unk: Vec<u8>,
142 #[fvec = 16]
143 _padding: Vec<u8>,
144}
145
146#[derive(Debug)]
147struct Entry<T: Read + Seek + std::fmt::Debug> {
148 header: BgiFileHeader,
149 reader: Arc<Mutex<T>>,
150 pos: usize,
151 base_offset: u64,
152 script_type: Option<ScriptType>,
153}
154
155impl<T: Read + Seek + std::fmt::Debug + Send + Sync> ArchiveContent for Entry<T> {
156 fn name(&self) -> &str {
157 &self.header.filename
158 }
159
160 fn size(&self) -> Option<u64> {
161 Some(self.header.size as u64)
162 }
163
164 fn script_type(&self) -> Option<&ScriptType> {
165 self.script_type.as_ref()
166 }
167
168 fn to_data<'a>(&'a mut self) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
169 Ok(Box::new(self))
170 }
171}
172
173impl<T: Read + Seek + std::fmt::Debug> Read for Entry<T> {
174 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
175 let mut reader = self.reader.lock().map_err(|e| {
176 std::io::Error::new(
177 std::io::ErrorKind::Other,
178 format!("Failed to lock mutex: {}", e),
179 )
180 })?;
181 reader.seek(SeekFrom::Start(
182 self.base_offset + self.header.offset as u64 + self.pos as u64,
183 ))?;
184 let bytes_read = buf.len().min(self.header.size as usize - self.pos);
185 if bytes_read == 0 {
186 return Ok(0);
187 }
188 let bytes_read = reader.read(&mut buf[..bytes_read])?;
189 self.pos += bytes_read;
190 Ok(bytes_read)
191 }
192}
193
194impl<T: Read + Seek + std::fmt::Debug> Seek for Entry<T> {
195 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
196 let new_pos = match pos {
197 SeekFrom::Start(offset) => offset as usize,
198 SeekFrom::End(offset) => {
199 if offset < 0 {
200 if (-offset) as usize > self.header.size as usize {
201 return Err(std::io::Error::new(
202 std::io::ErrorKind::InvalidInput,
203 "Seek from end exceeds file length",
204 ));
205 }
206 self.header.size as usize - (-offset) as usize
207 } else {
208 self.header.size as usize + offset as usize
209 }
210 }
211 SeekFrom::Current(offset) => {
212 if offset < 0 {
213 if (-offset) as usize > self.pos {
214 return Err(std::io::Error::new(
215 std::io::ErrorKind::InvalidInput,
216 "Seek from current exceeds current position",
217 ));
218 }
219 self.pos.saturating_sub((-offset) as usize)
220 } else {
221 self.pos + offset as usize
222 }
223 }
224 };
225 self.pos = new_pos;
226 Ok(self.pos as u64)
227 }
228
229 fn stream_position(&mut self) -> std::io::Result<u64> {
230 Ok(self.pos as u64)
231 }
232}
233
234struct MemEntry<F: Fn(&[u8], usize, &str) -> Option<&'static ScriptType>> {
235 name: String,
236 data: MemReader,
237 detect: F,
238}
239
240impl<F: Fn(&[u8], usize, &str) -> Option<&'static ScriptType>> Read for MemEntry<F> {
241 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
242 self.data.read(buf)
243 }
244}
245
246impl<F: Fn(&[u8], usize, &str) -> Option<&'static ScriptType>> ArchiveContent for MemEntry<F> {
247 fn name(&self) -> &str {
248 &self.name
249 }
250
251 fn size(&self) -> Option<u64> {
252 Some(self.data.data.len() as u64)
253 }
254
255 fn script_type(&self) -> Option<&ScriptType> {
256 (self.detect)(&self.data.data, self.data.data.len(), &self.name)
257 }
258
259 fn data(&mut self) -> Result<Vec<u8>> {
260 Ok(self.data.data.clone())
261 }
262
263 fn to_data<'a>(&'a mut self) -> Result<Box<dyn ReadSeek + 'a + Send + Sync>> {
264 Ok(Box::new(&mut self.data))
265 }
266}
267
268#[derive(Debug)]
269pub struct BgiArchive<'b, T: Read + Seek + std::fmt::Debug + 'b> {
271 reader: Arc<Mutex<T>>,
272 entries: Vec<BgiFileHeader>,
273 base_offset: u64,
274 #[cfg(feature = "bgi-img")]
275 is_sysgrp_arc: bool,
276 _mark: std::marker::PhantomData<&'b ()>,
277}
278
279impl<'b, T: Read + Seek + std::fmt::Debug + 'b> BgiArchive<'b, T> {
280 pub fn new(
287 mut reader: T,
288 archive_encoding: Encoding,
289 _config: &ExtraConfig,
290 _filename: &str,
291 ) -> Result<Self> {
292 let mut header = [0u8; 12];
293 reader.read_exact(&mut header)?;
294 if !header.starts_with(b"BURIKO ARC20") {
295 return Err(anyhow::anyhow!("Invalid BGI archive header"));
296 }
297
298 let file_count = reader.read_u32()?;
299 let mut entries = Vec::with_capacity(file_count as usize);
300 for _ in 0..file_count {
301 let entry = BgiFileHeader::unpack(&mut reader, false, archive_encoding, &None)?;
302 entries.push(entry);
303 }
304
305 #[cfg(feature = "bgi-img")]
306 let is_sysgrp_arc = _config.bgi_is_sysgrp_arc.unwrap_or_else(|| {
307 std::path::Path::new(&_filename.to_lowercase())
308 .file_name()
309 .map(|f| f == "sysgrp.arc")
310 .unwrap_or(false)
311 });
312
313 Ok(BgiArchive {
314 reader: Arc::new(Mutex::new(reader)),
315 entries,
316 base_offset: 16 + (file_count as u64 * 0x80),
317 #[cfg(feature = "bgi-img")]
318 is_sysgrp_arc,
319 _mark: std::marker::PhantomData,
320 })
321 }
322}
323
324impl<'b, T: Read + Seek + std::fmt::Debug + Send + Sync + 'b> Script for BgiArchive<'b, T> {
325 fn default_output_script_type(&self) -> OutputScriptType {
326 OutputScriptType::Json
327 }
328
329 fn default_format_type(&self) -> FormatOptions {
330 FormatOptions::None
331 }
332
333 fn is_archive(&self) -> bool {
334 true
335 }
336
337 fn iter_archive_filename<'a>(
338 &'a self,
339 ) -> Result<Box<dyn Iterator<Item = Result<String>> + 'a>> {
340 Ok(Box::new(
341 self.entries.iter().map(|e| Ok(e.filename.clone())),
342 ))
343 }
344
345 fn iter_archive_offset<'a>(&'a self) -> Result<Box<dyn Iterator<Item = Result<u64>> + 'a>> {
346 Ok(Box::new(self.entries.iter().map(|e| Ok(e.offset as u64))))
347 }
348
349 fn open_file<'a>(&'a self, index: usize) -> Result<Box<dyn ArchiveContent + Send + Sync + 'a>> {
350 if index >= self.entries.len() {
351 return Err(anyhow::anyhow!(
352 "Index out of bounds: {} (max: {})",
353 index,
354 self.entries.len()
355 ));
356 }
357 let entry = &self.entries[index];
358 let mut entry = Entry {
359 header: entry.clone(),
360 reader: self.reader.clone(),
361 pos: 0,
362 base_offset: self.base_offset,
363 script_type: None,
364 };
365 let mut buf = [0u8; 32];
366 match entry.read(&mut buf) {
367 Ok(_) => {}
368 Err(e) => {
369 return Err(anyhow::anyhow!(
370 "Failed to read entry '{}': {}",
371 entry.header.filename,
372 e
373 ));
374 }
375 }
376 entry.pos = 0;
377 if buf.starts_with(b"DSC FORMAT 1.00") {
378 let data = match entry.data() {
379 Ok(data) => data,
380 Err(e) => {
381 return Err(anyhow::anyhow!(
382 "Failed to read DSC data for '{}': {}",
383 entry.header.filename,
384 e
385 ));
386 }
387 };
388 entry.pos = 0;
389 let dsc = match DscDecoder::new(&data) {
390 Ok(dsc) => dsc,
391 Err(e) => {
392 return Err(anyhow::anyhow!(
393 "Failed to create DSC decoder for '{}': {}",
394 entry.header.filename,
395 e
396 ));
397 }
398 };
399 let decoded = match dsc.unpack() {
400 Ok(decoded) => decoded,
401 Err(e) => {
402 return Err(anyhow::anyhow!(
403 "Failed to unpack DSC data for '{}': {}",
404 entry.header.filename,
405 e
406 ));
407 }
408 };
409 let reader = MemReader::new(decoded);
410 if reader.data.starts_with(b"BSE 1.") {
411 match BseReader::new(reader, detect_script_type, &entry.header.filename) {
412 Ok(bse_reader) => {
413 return Ok(Box::new(bse_reader));
414 }
415 Err(e) => {
416 return Err(anyhow::anyhow!(
417 "Failed to create BSE reader for '{}': {}",
418 entry.header.filename,
419 e
420 ));
421 }
422 };
423 }
424 return Ok(Box::new(MemEntry {
425 name: entry.header.filename.clone(),
426 data: reader,
427 #[cfg(feature = "bgi-img")]
428 detect: if self.is_sysgrp_arc {
429 detect_script_type_sysgrp
430 } else {
431 detect_script_type
432 },
433 #[cfg(not(feature = "bgi-img"))]
434 detect: detect_script_type,
435 }));
436 }
437 if buf.starts_with(b"BSE 1.") {
438 let filename = entry.header.filename.clone();
439 #[cfg(feature = "bgi-img")]
440 let detect = if self.is_sysgrp_arc {
441 detect_script_type_sysgrp
442 } else {
443 detect_script_type
444 };
445 #[cfg(not(feature = "bgi-img"))]
446 let detect = detect_script_type;
447 match BseReader::new(entry, detect, &filename) {
448 Ok(mut bse_reader) => {
449 if bse_reader.is_dsc() {
450 let data = match bse_reader.data() {
451 Ok(data) => data,
452 Err(e) => {
453 return Err(anyhow::anyhow!(
454 "Failed to read BSE data for '{}': {}",
455 &filename,
456 e
457 ));
458 }
459 };
460 let dsc = match DscDecoder::new(&data) {
461 Ok(dsc) => dsc,
462 Err(e) => {
463 return Err(anyhow::anyhow!(
464 "Failed to create DSC decoder for '{}': {}",
465 &filename,
466 e
467 ));
468 }
469 };
470 let decoded = match dsc.unpack() {
471 Ok(decoded) => decoded,
472 Err(e) => {
473 return Err(anyhow::anyhow!(
474 "Failed to unpack DSC data for '{}': {}",
475 &filename,
476 e
477 ));
478 }
479 };
480 let reader = MemReader::new(decoded);
481 return Ok(Box::new(MemEntry {
482 name: filename,
483 data: reader,
484 detect,
485 }));
486 }
487 return Ok(Box::new(bse_reader));
488 }
489 Err(e) => {
490 return Err(anyhow::anyhow!(
491 "Failed to create BSE reader for '{}': {}",
492 &filename,
493 e
494 ));
495 }
496 };
497 }
498 #[cfg(feature = "bgi-img")]
499 if self.is_sysgrp_arc {
500 entry.script_type = Some(ScriptType::BGIImg);
501 } else {
502 entry.script_type =
503 detect_script_type(&buf, buf.len(), &entry.header.filename).cloned();
504 }
505 #[cfg(not(feature = "bgi-img"))]
506 {
507 entry.script_type =
508 detect_script_type(&buf, buf.len(), &entry.header.filename).cloned();
509 }
510 Ok(Box::new(entry))
511 }
512}
513
514fn detect_script_type(buf: &[u8], buf_len: usize, filename: &str) -> Option<&'static ScriptType> {
515 if buf_len >= 28 && buf.starts_with(b"BurikoCompiledScriptVer1.00\0") {
516 return Some(&ScriptType::BGI);
517 }
518 #[cfg(feature = "bgi-img")]
519 if buf_len >= 16 && buf.starts_with(b"CompressedBG___") {
520 return Some(&ScriptType::BGICbg);
521 }
522 #[cfg(feature = "bgi-audio")]
523 if buf_len >= 8 && buf[4..].starts_with(b"bw ") {
524 return Some(&ScriptType::BGIAudio);
525 }
526 let filename = filename.to_lowercase();
527 if filename.ends_with("._bp") {
528 return Some(&ScriptType::BGIBp);
529 } else if filename.ends_with("._bsi") {
530 return Some(&ScriptType::BGIBsi);
531 }
532 None
533}
534
535#[cfg(feature = "bgi-img")]
536fn detect_script_type_sysgrp(
537 _buf: &[u8],
538 _buf_len: usize,
539 _filename: &str,
540) -> Option<&'static ScriptType> {
541 Some(&ScriptType::BGIImg)
542}
543
544pub struct BgiArchiveWriter<T: Write + Seek> {
546 writer: Arc<Mutex<T>>,
547 headers: Arc<Mutex<HashMap<String, BgiFileHeader>>>,
548 compress_file: bool,
549 encoding: Encoding,
550 compress_level: u8,
551 runner: ThreadPool<Result<()>>,
552}
553
554impl<T: Write + Seek> BgiArchiveWriter<T> {
555 pub fn new(
562 mut writer: T,
563 files: &[&str],
564 encoding: Encoding,
565 config: &ExtraConfig,
566 ) -> Result<Self> {
567 writer.write_all(b"BURIKO ARC20")?;
568 let file_count = files.len();
569 writer.write_u32(file_count as u32)?;
570 let mut headers = HashMap::new();
571 for file in files {
572 let header = BgiFileHeader {
573 filename: file.to_string(),
574 offset: 0,
575 size: 0,
576 _unk: vec![0; 8],
577 _padding: vec![0; 16],
578 };
579 header.pack(&mut writer, false, encoding, &None)?;
580 headers.insert(file.to_string(), header);
581 }
582 Ok(BgiArchiveWriter {
583 writer: Arc::new(Mutex::new(writer)),
584 headers: Arc::new(Mutex::new(headers)),
585 compress_file: config.bgi_compress_file,
586 encoding,
587 compress_level: config.bgi_compress_level,
588 runner: ThreadPool::new(
589 if config.bgi_compress_file {
590 config.bgi_arc_workers
591 } else {
592 1
593 },
594 Some("bgi-arc-writer"),
595 false,
596 )?,
597 })
598 }
599}
600
601impl<T: Write + Seek + Send + Sync + 'static> Archive for BgiArchiveWriter<T> {
602 fn new_file<'a>(
603 &'a mut self,
604 name: &str,
605 size: Option<u64>,
606 ) -> Result<Box<dyn WriteSeek + 'a>> {
607 let mut entry = self
608 .headers
609 .lock_blocking()
610 .get(name)
611 .ok_or_else(|| anyhow::anyhow!("File '{}' not found in archive", name))?
612 .clone();
613 if self.compress_file {
614 let inner = self.new_file_non_seek(name, size)?;
615 Ok(Box::new(Writer {
616 inner,
617 mem: MemWriter::new(),
618 }))
619 } else {
620 let mut writer = self.writer.lock_blocking();
621 let offset = writer.seek(SeekFrom::End(0))?;
622 entry.offset = offset as u32;
623 Ok(Box::new(BgiArchiveFile {
624 header: entry,
625 writer: self.writer.clone(),
626 pos: 0,
627 headers: self.headers.clone(),
628 name: name.to_owned(),
629 }))
630 }
631 }
632
633 fn new_file_non_seek<'a>(
634 &'a mut self,
635 name: &str,
636 size: Option<u64>,
637 ) -> Result<Box<dyn Write + 'a>> {
638 if !self.compress_file {
639 return Ok(Box::new(self.new_file(name, size)?));
640 }
641 for err in self.runner.take_results() {
642 err?;
643 }
644 let mut entry = self
645 .headers
646 .lock_blocking()
647 .get(name)
648 .ok_or_else(|| anyhow::anyhow!("File '{}' not found in archive", name))?
649 .clone();
650 let (reader, writer) = std::io::pipe()?;
651 let file = self.writer.clone();
652 let headers = self.headers.clone();
653 let compress_level = self.compress_level;
654 let name = name.to_owned();
655 self.runner.execute(
656 move |_| {
657 let mut reader = reader;
658 let mut data = Vec::new();
659 reader.read_to_end(&mut data)?;
660 let mut buf = MemWriter::new();
661 {
662 let mut b = std::io::BufWriter::new(&mut buf);
663 DscEncoder::new(&mut b, compress_level).pack(&data)?;
664 }
665 let mut writer = file.lock_blocking();
666 let offset = writer.seek(SeekFrom::End(0))?;
667 entry.offset = offset as u32;
668 writer.write_all(&buf.data)?;
669 entry.size = buf.data.len() as u32;
670 headers.lock_blocking().insert(name, entry);
671 Ok(())
672 },
673 true,
674 )?;
675 Ok(Box::new(writer))
676 }
677
678 fn write_header(&mut self) -> Result<()> {
679 self.runner.join();
680 for err in self.runner.take_results() {
681 err?;
682 }
683 let mut writer = self.writer.lock_blocking();
684 let mut headers = self.headers.lock_blocking();
685 writer.seek(SeekFrom::Start(0x10))?;
686 let base_offset = headers.len() as u32 * 0x80 + 16;
687 let mut files = headers.iter_mut().map(|(_, d)| d).collect::<Vec<_>>();
688 files.sort_by_key(|f| f.offset);
689 for file in files {
690 file.offset -= base_offset;
691 file.pack(writer.deref_mut(), false, self.encoding, &None)?;
692 }
693 Ok(())
694 }
695}
696
697pub struct BgiArchiveFile<T: Write + Seek> {
699 header: BgiFileHeader,
700 writer: Arc<Mutex<T>>,
701 pos: usize,
702 headers: Arc<Mutex<HashMap<String, BgiFileHeader>>>,
703 name: String,
704}
705
706impl<T: Write + Seek> Write for BgiArchiveFile<T> {
707 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
708 let mut writer = self.writer.lock_blocking();
709 writer.seek(SeekFrom::Start(self.header.offset as u64 + self.pos as u64))?;
710 let bytes_written = writer.write(buf)?;
711 self.pos += bytes_written;
712 self.header.size = self.header.size.max(self.pos as u32);
713 Ok(bytes_written)
714 }
715
716 fn flush(&mut self) -> std::io::Result<()> {
717 self.writer.lock_blocking().flush()
718 }
719}
720
721impl<T: Write + Seek> Seek for BgiArchiveFile<T> {
722 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
723 let new_pos = match pos {
724 SeekFrom::Start(offset) => offset as usize,
725 SeekFrom::End(offset) => {
726 if offset < 0 {
727 if (-offset) as usize > self.header.size as usize {
728 return Err(std::io::Error::new(
729 std::io::ErrorKind::InvalidInput,
730 "Seek from end exceeds file length",
731 ));
732 }
733 self.header.size as usize - (-offset) as usize
734 } else {
735 self.header.size as usize + offset as usize
736 }
737 }
738 SeekFrom::Current(offset) => {
739 if offset < 0 {
740 if (-offset) as usize > self.pos {
741 return Err(std::io::Error::new(
742 std::io::ErrorKind::InvalidInput,
743 "Seek from current exceeds current position",
744 ));
745 }
746 self.pos.saturating_sub((-offset) as usize)
747 } else {
748 self.pos + offset as usize
749 }
750 }
751 };
752 self.pos = new_pos;
753 Ok(self.pos as u64)
754 }
755}
756
757impl<T: Write + Seek> Drop for BgiArchiveFile<T> {
758 fn drop(&mut self) {
759 self.headers
760 .lock_blocking()
761 .insert(self.name.clone(), self.header.clone());
762 }
763}
764struct Writer<'a> {
765 inner: Box<dyn Write + 'a>,
766 mem: MemWriter,
767}
768
769impl std::fmt::Debug for Writer<'_> {
770 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
771 f.debug_struct("Writer").field("mem", &self.mem).finish()
772 }
773}
774
775impl<'a> Write for Writer<'a> {
776 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
777 self.mem.write(buf)
778 }
779
780 fn flush(&mut self) -> std::io::Result<()> {
781 self.mem.flush()
782 }
783}
784
785impl<'a> Seek for Writer<'a> {
786 fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
787 self.mem.seek(pos)
788 }
789
790 fn stream_position(&mut self) -> std::io::Result<u64> {
791 self.mem.stream_position()
792 }
793
794 fn rewind(&mut self) -> std::io::Result<()> {
795 self.mem.rewind()
796 }
797}
798
799impl<'a> Drop for Writer<'a> {
800 fn drop(&mut self) {
801 let _ = self.inner.write_all(&self.mem.data);
802 let _ = self.inner.flush();
803 }
804}